#include <cstdio>
#include <cmath>
#include <bitmap_image.hpp>

unsigned int Height, Width;

const float Verticle_core[9] = {-1.0,   0, 1.0, -2.0,    0, 2.0, -1.0,    0,  1.0},
			Parallel_core[9] = { 1.0, 2.0, 1.0,    0,    0,   0, -1.0, -2.0, -1.0},
			Lagrange_core[9] = {   0, 1.0,   0,  1.0, -4.0, 1.0,    0,  1.0,    0};
			
const int Directionx[9] = {-1, -1, -1,  0, 0, 0,  1, 1, 1},
		  Directiony[9] = {-1,  0,  1, -1, 0, 1, -1, 0, 1};

const float Linear_offset = 2.0, Lagrange_offset = 10.0;
const float cutoff = 50.0;

bool get_Black_fix(unsigned int i, unsigned int j, unsigned int k) {
	if(i + Directionx[k] < 0 || i + Directionx[k] >= Height ||
	   j + Directiony[k] < 0 || j + Directiony[k] >= Width) return false;
	return true;
}

unsigned char RGB_to_Black(rgb_t color) {
	return round((color.red + color.green + color.blue) / 3.0);
}

rgb_t Black_to_RGB(float black) {
	black += 127;
	if(black > 255) black = 255;
	if(black < 0) black = 0;
	rgb_t res;
	res.red = res.green = res.blue = round(black);
	return res;
}

rgb_t Black_to_RGB_fix(float black) {
	black = abs(round(black)) - cutoff;
	if(black > 255) black = 255;
	if(black < 0) black = 0;
	rgb_t res;
	res.red = res.green = res.blue = round(black);
	return res;
}

int main() {
	bitmap_image Input("Input.bmp");
	
    if(!Input) {
       printf("Error - Failed to open: input.bmp\n");
       return 1;
    }
	// printf("Image read successfully!\n");
	
	// printf("Image size: %d * %d\n", Height = Input.height(), Width = Input.width());
	Height = Input.height();
	Width = Input.width();
							 
	//float Verticle_res[Height][Width], Parallel_res[Height][Width], Lagrange_res[Height][Width];
	float Verticle_res, Parallel_res, Lagrange_res;
	
	bitmap_image Verticle(Width, Height);
	bitmap_image Parallel(Width, Height);
	bitmap_image Lagrange(Width, Height);
	bitmap_image Verticle_fix(Width, Height);
	bitmap_image Parallel_fix(Width, Height);
	bitmap_image Lagrange_fix(Width, Height);
	// printf("Convolution initialized successfully!\n");
	
	unsigned int i, j, k;//i -> row, j -> column
	for(i = 0; i < Height; i++)
		for(j = 0; j < Width; j++) {
			Verticle_res = Parallel_res = Lagrange_res = 0;
			for(k = 0; k < 9; k++) {
				if(!get_Black_fix(i, j, k)) continue;
				Verticle_res += RGB_to_Black(Input.get_pixel(j + Directiony[k], i + Directionx[k])) * Verticle_core[k];
				Parallel_res += RGB_to_Black(Input.get_pixel(j + Directiony[k], i + Directionx[k])) * Parallel_core[k];
				Lagrange_res += RGB_to_Black(Input.get_pixel(j + Directiony[k], i + Directionx[k])) * Lagrange_core[k];
			}
			Verticle_res *= Linear_offset;
			Parallel_res *= Linear_offset;
			Lagrange_res *= Lagrange_offset;
			Verticle.set_pixel(j, i, Black_to_RGB(Verticle_res));
			Parallel.set_pixel(j, i, Black_to_RGB(Parallel_res));
			Lagrange.set_pixel(j, i, Black_to_RGB(Lagrange_res));
			Verticle_fix.set_pixel(j, i, Black_to_RGB_fix(Verticle_res));
			Parallel_fix.set_pixel(j, i, Black_to_RGB_fix(Parallel_res));
			Lagrange_fix.set_pixel(j, i, Black_to_RGB_fix(Lagrange_res));
			//printf("Convoluting %d %d, result %f %f %f\n", i, j, Verticle_res, Parallel_res, Lagrange_res);
		}
	// printf("Convolution run successfully!\n");
	// Verticle.save_image("Verticle.bmp");
	// Parallel.save_image("Parallel.bmp");
	// Lagrange.save_image("Lagrange.bmp");
	Verticle_fix.save_image("Verticle_fix.bmp");
	// Parallel_fix.save_image("Parallel_fix.bmp");
	// Lagrange_fix.save_image("Lagrange_fix.bmp");
	return 0;
}
